#include "stdafx.h"
#include "ring.h"

int ring::_thread_index = 0;
int ring::_loop_index = 0;
DWORD ring::_wait_object_result = 0;
HANDLE ring::_thread_wait[2] = {0};


#define _thread_state				_states[_thread_index]
#define _thread_other_ring_state	((ring_states)(1 - ((byte)(_thread_state._state))))
#define _waiting_group_as_ring_state (_waiting_group ? ring_states::active_group_1 : ring_states::active_group_0)

#define _waiting_group_0			false
#define _waiting_group_1			true

//---------------------------------------------------------------------------
void ring::register_to_ring()
{
	std::unique_lock<std::mutex> ul(_registration_lock);
	_states.push_back(thread_state());
	
	_thread_index = (int)(_states.size() - 1); // give the thread its index in the vector
	_loop_index = 0;
	_wait_object_result = 0;
	ZeroMemory(_thread_wait, sizeof(HANDLE)*2);
}
//---------------------------------------------------------------------------
void ring::pre(void)
{
	// make thread active
	_thread_state = ring_states::active;

	// assign thread to group
	_thread_state = _waiting_group_as_ring_state;

	if(_thread_state != ring_states::active_group_0 && _thread_state != ring_states::active_group_1)
	{
		throw std::exception("internal error - invalid ring group");
	}

	for(_loop_index = 0; _loop_index < (int)_states.size() ; _loop_index++)
	{
		if(_loop_index == _thread_index)
		{
			continue;
		}

		// if my group's turn arrive - go right in!
		if(_thread_state != _waiting_group_as_ring_state)
		{
			break;
		}

		// -- NOT my group's turn --

		// if other thread is active and group not assigned, wait until is has a group
		/* while(_states[i] == ring_states::active){ sleep } */
		_wait_object_result = ::WaitForSingleObject(_states[_loop_index]._is_assigned_or_inactive->_wait_for_true, INFINITE); // wait until thread is assigned or inactive
		if(_wait_object_result == WAIT_FAILED)
		{
			printf("WaitForSingleObject error: 0x%x\r\n", ::GetLastError());
			throw std::exception("Failed WaitForSingleObject!");
		}
		
		// if other thread is in the other group (that is not waiting)
		// wait until done OR my group's turn
		
		// while thread i is still active+other group AND my group waits - wait...!
		//while(_states[i] != (ring_states)(1 - ((byte)(_thread_state))) || _thread_state != _waiting_group)
		_thread_wait[0] = _thread_state == ring_states::active_group_1 ? 
									_states[_loop_index]._is_group_0->_wait_for_false : _states[_loop_index]._is_group_1->_wait_for_false;
		
		_thread_wait[1] = _thread_state == ring_states::active_group_0 ? _waiting_group.wait_for(_waiting_group_1) : _waiting_group.wait_for(_waiting_group_0);
		
		_wait_object_result = ::WaitForMultipleObjects(2, _thread_wait, FALSE, 60000);
		if(_wait_object_result == WAIT_OBJECT_0){ 
			//printf("other thread done\r\n");
		}
		else if(_wait_object_result == WAIT_OBJECT_0+1){ 
			//printf("my group's turn\r\n");
		}
		else if(_wait_object_result == WAIT_FAILED)
		{
			printf("WaitForSingleObject error: 0x%x\r\n", ::GetLastError());
			throw std::exception("Failed WaitForSingleObject!");
		}
		else if(_wait_object_result == WAIT_TIMEOUT)
		{
			printf("ID: %d : Timeout: belongs to waiting group: %d ; current waiting group: %d ; ", GetCurrentThreadId(), (int)((ring_states)_thread_state), (int)_waiting_group);
			
			printf("waiting on 0x%x ; Group 0 HANDLE: 0x%x ; Group 1 HANDLE: 0x%x\r\n", _thread_wait[1], _waiting_group._wait_for_false, _waiting_group._wait_for_true);

			// check HANLE STATE
			_wait_object_result = ::WaitForSingleObject(_thread_wait[1], 3000);
			if(_wait_object_result == WAIT_TIMEOUT)
			{
				printf("ID: %d : HANDLE 0x%x works\r\n", ::GetCurrentThreadId(), _thread_wait[1]);
			}
			
			throw std::exception("Timeout");
		}
	}
}
//---------------------------------------------------------------------------
void ring::post(void)
{
	// switch the groups
	_waiting_group = _thread_state == ring_states::active_group_0 ? _waiting_group_1 : _waiting_group_0;

	/*
	_waiting_group._bool = _thread_state == ring_states::active_group_0 ? _waiting_group_1 : _waiting_group_0;
	if(_thread_state == ring_states::active_group_0)
	{
		::ResetEvent(_waiting_group._wait_for_false);
		::SetEvent(_waiting_group._wait_for_true);
	}
	else
	{
		::ResetEvent(_waiting_group._wait_for_true);
		::SetEvent(_waiting_group._wait_for_false);
	}
	*/
	/*
	if(::WaitForSingleObject(_waiting_group._wait_for_false, 1) == WAIT_TIMEOUT &&
		::WaitForSingleObject(_waiting_group._wait_for_true, 1) == WAIT_TIMEOUT)
	{
		printf("SOMETHING IS REALLY WRONG!!!\r\n");
		::DebugBreak();
		ExitProcess(1);
	}
	*/
	//printf("ID: %d : Waiting group: %d\r\n", ::GetCurrentThreadId(), (bool)_waiting_group ? 1 : 0);
	
	// make thread inactive
	_thread_state = ring_states::inactive;
}
//---------------------------------------------------------------------------

